﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Net;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.Diagnostics;

namespace GalaxyWars_Server
{
    class Server
    {
        private int playerCount = 0;

        public List<Tuple<string, string, TcpClient>> connectedClients;
        Object game = new Object();
        byte[] gamebyte;
        Boolean hostloggedin = false;
        Boolean gameStarted = false;
        string hostplayer;
        string hisTurn;

        public Server(string[] args)
        {
            Console.CancelKeyPress += new ConsoleCancelEventHandler(consoleClose);

            connectedClients = new List<Tuple<string, string, TcpClient>>();
            IPAddress localhost;

            localhost = IPAddress.Parse("127.0.0.1");

            TcpListener listener = new TcpListener(localhost, 2750);
            listener.Start();
            Boolean serveren = true;
            while (serveren)
            {
                if (!listener.Pending())
                {
                    if (gameStarted)
                    {
                        serveren = false;
                    }
                }
                else
                {

                    Console.WriteLine("GalaxyWars Server is Waiting For a New Connection, connections : ...");
                    TcpClient client = listener.AcceptTcpClient();
                    Thread thread = new Thread(HandleClientThread);
                    thread.Start(client);

                }
            }
            handleGame();
        }

        public void consoleClose(object obj, ConsoleCancelEventArgs args)
        {
            Console.WriteLine("==============================");
            foreach (var tuple in connectedClients)
            {
                SendMessage(tuple.Item3, "exitgame");
            }
        }


        private void handleGame() {
            Boolean done = false;
            while (!done)
            {
                foreach (Tuple<string, string, TcpClient> player in connectedClients)
                {
                    if(player.Item2.Equals(hisTurn)){
                        //Send to client that he has turn
                        Console.WriteLine("Player {0} has turn",player.Item2);
                        SendMessage(player.Item3,"?Turn");
                        Console.WriteLine("Send the turn message");
                        gamebyte = ReadByteArray(player.Item3);
                        Console.WriteLine("Received the game data form player: " + player.Item2);
                        //after his turn give all other clients the data
                        Console.WriteLine("Sending to all others the game data");
                        foreach (Tuple<string, string, TcpClient> p in connectedClients)
                        {
                            if(!p.Item2.Equals(hisTurn)){
                                SendByteArray(p.Item3,gamebyte);
                            }
                        }
                        Console.WriteLine("Sending to all others complete now giving a other player the turn");
                        //Give turn to next client
                        if (connectedClients.Count > connectedClients.IndexOf(player))
                        {
                            //If List is bigger than the index of the player than the next player is the next client in the list
                            hisTurn = connectedClients.ElementAt(connectedClients.IndexOf(player) + 1).Item2;
                        }
                        else
                        {
                            //Else the nextplayer is at index zero
                            hisTurn = connectedClients.ElementAt(0).Item2;
                        }
                    }
                }
            }
        }


        public void HandleClientThread(object obj)
        {
            Console.WriteLine("started new thread for client");

            TcpClient client = obj as TcpClient;

            //Get playerName
            SendMessage(client, "?ID");
            string playerIdentification = ReadMessage(client);

            Console.WriteLine("Login ok for Player: " + playerIdentification);

            //Let the player know what his playerNumber is
            string playerNumber = playerCount + "";
            SendMessage(client, "?IDNR" + playerNumber);
            Console.WriteLine("IDNR");
            ReadMessage(client);

            playerCount++;
            connectedClients.Add(new Tuple<string, string, TcpClient>(playerIdentification, playerNumber, client));

            foreach(var tuple in connectedClients)
            {
                Console.WriteLine("PlayerID : {0}, PlayerNumber : {1}", tuple.Item1, tuple.Item2);
            }

            //to ensure good transfer of the data
            //Get Game Type: HOST/ JOIN
            SendMessage(client, "?TYPE");
            Console.WriteLine("TYPE");
            string playertype = ReadMessage(client);
            Console.WriteLine(playertype);

            //Get the date from the host
            if (playertype == "!HOST") {
                Console.WriteLine("Client = HOST");
                SendMessage(client, "?GAME");
                Console.WriteLine("Send ?GAME");
                gamebyte = ReadByteArray(client);
                Console.WriteLine("Array size:" + gamebyte.Length);
                hostplayer = playerNumber;
                hostloggedin = true;
                //game = receiveObject(client);

                //saveData(alleDataVanDeGame as string, playerIdentification);
                
            }
            //Send the data to the joining game player
            else if (playertype == "!JOIN") {
                Console.WriteLine("Client = JOIN");
                SendMessage(client, "?GAME");
                SendByteArray(client, gamebyte);
                //sendObject(game,client);
            }


            //If player type is !HOST then wait for start game command
            if(playertype == "!HOST"){
            bool done = false;
            while (!done)
            {

                try
                {
                    //Console.WriteLine("Awaiting start command from the host");
                    string received = ReadMessage(client);
                    //Console.WriteLine("Received: {0}", received.ToString());
                    done = received.Equals("!START");
                }
                catch (NullReferenceException) { Debug.WriteLine("Client Disconnected"); }
                
            }
            gameStarted = true;
            //client.Close();
            //Console.WriteLine("Connection closed");
            }
        }

        private string ReadMessage(TcpClient client)
        {
            //byte[] buffer = new byte[512];
            byte[] buffer = new byte[client.ReceiveBufferSize];
            int totalRead = 0;
            //read bytes until stream indicates there are no more
            do
            {
                try
                {
                    int read = client.GetStream().Read(buffer, totalRead, buffer.Length - totalRead);
                    totalRead += read;
                }
                catch (Exception) { Debug.WriteLine("Client connection lost"); return null; }
                //Console.WriteLine("ReadMessage: " + read);
            } while (client.GetStream().DataAvailable);


            return Encoding.Unicode.GetString(buffer, 0, totalRead);
        }

        public void SendMessage(TcpClient client, string message)
        {
            try
            {
                byte[] bytes = Encoding.Unicode.GetBytes(message);
                client.GetStream().Write(bytes, 0, bytes.Length);
            }
            catch (IOException)
            {

            }
            catch (InvalidOperationException)
            {

            }
        }

        //Send Objects to clients
        public void sendObject(Object o,TcpClient client) 
        {
            //try
            //{
                IFormatter formatter = new BinaryFormatter();

                NetworkStream strm = client.GetStream();
                formatter.Serialize(strm, o);

                strm.Close();
            //}
            //catch (IOException) { Console.WriteLine("Connection Error send Object"); }
        }

        //Receive Objects from .....
        public object receiveObject(TcpClient client) 
        {
            Object o = null;
            try
            {
                NetworkStream strm = client.GetStream();
                IFormatter formatter = new BinaryFormatter();

                o = (Object)formatter.Deserialize(strm);

                strm.Close();
            }
            catch (Exception) { Console.WriteLine("Connection Error in receive object"); }
            return o;
        }

        //Send the byte array
        public void SendByteArray(TcpClient client, byte[] arrBytes)
        {
            try
            {
                //byte[] bytes = arrBytes;
                //byte[] bytes = Encoding.Unicode.GetBytes(message);
                client.GetStream().Write(arrBytes, 0, arrBytes.Length);
            }
            catch (IOException)
            {

            }
            catch (InvalidOperationException)
            {

            }
        }

        //Receive the byte array
        private byte[] ReadByteArray(TcpClient client)
        {
            //byte[] buffer = new byte[2048];
            Console.WriteLine("In readbytearray");
            byte[] buffer = new byte[5242880]; //5 mebibyte
            //Console.WriteLine("RecBuff: >{0}<",client.ReceiveBufferSize);
            int totalRead = 0;
            //read bytes until stream indicates there are no more
            do
            {
                try
                {
                    int read = client.GetStream().Read(buffer, totalRead, buffer.Length - totalRead);
                    totalRead += read;
                }
                catch (Exception) { Debug.WriteLine("Client connection lost"); return null; }
                //Console.WriteLine("ReadMessage: " + read);
            } while (client.GetStream().DataAvailable);

            Console.WriteLine("Returning from readbytearray");
            return buffer;//Encoding.Unicode.GetString(buffer, 0, totalRead);
        }

        private static void saveData(String alleData, String hostingPlayer)
        {
            try
            {
                BinaryFormatter binaryFormatter = new BinaryFormatter();
                FileStream fileStream = new FileStream(hostingPlayer + ".txt"/**".hpgf"*/, FileMode.Create);
                binaryFormatter.Serialize(fileStream, alleData);

                fileStream.Dispose();
            }
            catch (Exception e)
            {
                Console.WriteLine("En Nu Ga Ik Dood");
            }
        }
    }
}